﻿namespace Nomo.UnityCoreModule.UI
{
    /// <summary>
    /// 圆角矩形。
    /// </summary>
    public sealed class RoundedRectangle : UnityEngine.UI.MaskableGraphic, UnityEngine.UI.ILayoutElement
    {
        [UnityEngine.SerializeField]                            private UnityEngine.Texture texture;
        [UnityEngine.SerializeField] [UnityEngine.Range(1, 32)] private int                 segments   = 16;
        [UnityEngine.SerializeField]                            private bool                normalized = true;
        [UnityEngine.SerializeField] [UnityEngine.Min(0F)]      private float               upperLeftCornerRadius;
        [UnityEngine.SerializeField] [UnityEngine.Min(0F)]      private float               upperRightCornerRadius;
        [UnityEngine.SerializeField] [UnityEngine.Min(0F)]      private float               lowerLeftCornerRadius;
        [UnityEngine.SerializeField] [UnityEngine.Min(0F)]      private float               lowerRightCornerRadius;

        /// <inheritdoc />
        public override UnityEngine.Texture mainTexture => texture ? texture : material && material.mainTexture ? material.mainTexture : s_WhiteTexture;

        /// <summary>
        /// 纹理。
        /// </summary>
        public UnityEngine.Texture Texture
        {
            get => texture;
            set
            {
                if (texture == value)
                {
                    return;
                }
                texture = value;
                SetVerticesDirty();
                SetMaterialDirty();
            }
        }

        /// <summary>
        /// 边数。
        /// </summary>
        public int Segments
        {
            get => segments;
            set
            {
                if (segments == value)
                {
                    return;
                }
                segments = value;
                SetVerticesDirty();
            }
        }

        /// <summary>
        /// 是否使用标准化单位长度来表示内圆的直径？
        /// </summary>
        public bool Normalized
        {
            get => normalized;
            set
            {
                if (normalized == value)
                {
                    return;
                }
                var r       = GetPixelAdjustedRect();
                var minSide = UnityEngine.Mathf.Min(r.width, r.height);
                switch (value)
                {
                    case true:
                        upperLeftCornerRadius  = minSide != 0F ? UnityEngine.Mathf.Clamp(upperLeftCornerRadius  / minSide, 0F, 0.5F) : 0F;
                        upperRightCornerRadius = minSide != 0F ? UnityEngine.Mathf.Clamp(upperRightCornerRadius / minSide, 0F, 0.5F) : 0F;
                        lowerLeftCornerRadius  = minSide != 0F ? UnityEngine.Mathf.Clamp(lowerLeftCornerRadius  / minSide, 0F, 0.5F) : 0F;
                        lowerRightCornerRadius = minSide != 0F ? UnityEngine.Mathf.Clamp(lowerRightCornerRadius / minSide, 0F, 0.5F) : 0F;
                        break;
                    default:
                        upperLeftCornerRadius  *= minSide;
                        upperRightCornerRadius *= minSide;
                        lowerLeftCornerRadius  *= minSide;
                        lowerRightCornerRadius *= minSide;
                        break;
                }
                normalized = value;
                SetVerticesDirty();
            }
        }

        /// <summary>
        /// 顶点半径（顺序：左上顶点、右上顶点、左下顶点、右下顶点）。
        /// </summary>
        public UnityEngine.Vector4 CornerRadii
        {
            get => new UnityEngine.Vector4(upperLeftCornerRadius, upperRightCornerRadius, lowerLeftCornerRadius, lowerRightCornerRadius);
            set
            {
                var verticesDirty = false;
                if (upperLeftCornerRadius != value.x)
                {
                    verticesDirty         = true;
                    upperLeftCornerRadius = value.x;
                }
                if (upperRightCornerRadius != value.y)
                {
                    verticesDirty         = true;
                    upperLeftCornerRadius = value.y;
                }
                if (lowerLeftCornerRadius != value.z)
                {
                    verticesDirty         = true;
                    upperLeftCornerRadius = value.z;
                }
                if (lowerRightCornerRadius != value.w)
                {
                    verticesDirty         = true;
                    upperLeftCornerRadius = value.w;
                }
                if (verticesDirty)
                {
                    SetVerticesDirty();
                }
            }
        }

        /// <summary>
        /// 左上顶点半径。
        /// </summary>
        public float UpperLeftCornerRadius
        {
            get => upperLeftCornerRadius;
            set
            {
                if (upperLeftCornerRadius == value)
                {
                    return;
                }
                upperLeftCornerRadius = value;
                SetVerticesDirty();
            }
        }

        /// <summary>
        /// 右上顶点半径。
        /// </summary>
        public float UpperRightCornerRadius
        {
            get => upperRightCornerRadius;
            set
            {
                if (upperRightCornerRadius == value)
                {
                    return;
                }
                upperRightCornerRadius = value;
                SetVerticesDirty();
            }
        }

        /// <summary>
        /// 左下顶点半径。
        /// </summary>
        public float LowerLeftCornerRadius
        {
            get => lowerLeftCornerRadius;
            set
            {
                if (lowerLeftCornerRadius == value)
                {
                    return;
                }
                lowerLeftCornerRadius = value;
                SetVerticesDirty();
            }
        }

        /// <summary>
        /// 右下顶点半径。
        /// </summary>
        public float LowerRightCornerRadius
        {
            get => lowerRightCornerRadius;
            set
            {
                if (lowerRightCornerRadius == value)
                {
                    return;
                }
                lowerRightCornerRadius = value;
                SetVerticesDirty();
            }
        }

        /// <inheritdoc />
        public override void SetNativeSize()
        {
            rectTransform.anchorMin = rectTransform.anchorMax = new UnityEngine.Vector2(0.5F, 0.5F);
            var t                                             = mainTexture;
            rectTransform.sizeDelta = new UnityEngine.Vector2(t.width, t.height);
        }

        /// <inheritdoc />
        protected override void OnPopulateMesh(UnityEngine.UI.VertexHelper vh)
        {
            if (upperRightCornerRadius == 0F && upperLeftCornerRadius == 0F && lowerLeftCornerRadius == 0F && lowerRightCornerRadius == 0F)
            {
                base.OnPopulateMesh(vh);
                return;
            }

            vh.Clear();

            var r          = GetPixelAdjustedRect();
            var center     = r.center;
            var halfWidth  = r.width  * 0.5F;
            var halfHeight = r.height * 0.5F;
            var xMin       = r.xMin;
            var xMax       = r.xMax;
            var yMin       = r.yMin;
            var yMax       = r.yMax;
            var minSide    = UnityEngine.Mathf.Min(r.width, r.height);

            float ul;
            float ur;
            float ll;
            float lr;

            switch (normalized)
            {
                case true:
                    ul = upperLeftCornerRadius  = UnityEngine.Mathf.Clamp(upperLeftCornerRadius,  0F, 0.5F);
                    ur = upperRightCornerRadius = UnityEngine.Mathf.Clamp(upperRightCornerRadius, 0F, 0.5F);
                    ll = lowerLeftCornerRadius  = UnityEngine.Mathf.Clamp(lowerLeftCornerRadius,  0F, 0.5F);
                    lr = lowerRightCornerRadius = UnityEngine.Mathf.Clamp(lowerRightCornerRadius, 0F, 0.5F);
                    break;
                default:
                    ul = UnityEngine.Mathf.Clamp((upperLeftCornerRadius  = UnityEngine.Mathf.Max(upperLeftCornerRadius,  0F)) / minSide, 0F, 0.5F);
                    ur = UnityEngine.Mathf.Clamp((upperRightCornerRadius = UnityEngine.Mathf.Max(upperRightCornerRadius, 0F)) / minSide, 0F, 0.5F);
                    ll = UnityEngine.Mathf.Clamp((lowerLeftCornerRadius  = UnityEngine.Mathf.Max(lowerLeftCornerRadius,  0F)) / minSide, 0F, 0.5F);
                    lr = UnityEngine.Mathf.Clamp((lowerRightCornerRadius = UnityEngine.Mathf.Max(lowerRightCornerRadius, 0F)) / minSide, 0F, 0.5F);
                    break;
            }

            var uiVertex = UnityEngine.UIVertex.simpleVert;
            uiVertex.color = color;

            uiVertex.position = UnityEngine.Vector2.zero + center;
            uiVertex.uv0      = new UnityEngine.Vector2(UnityEngine.Mathf.InverseLerp(xMin, xMax, uiVertex.position.x), UnityEngine.Mathf.InverseLerp(yMin, yMax, uiVertex.position.y));
            vh.AddVert(uiVertex);

            var stepAngle = UnityEngine.Mathf.PI * 2F / (segments * 4);
            for (var i = 0; i < 4; i++)
            {
                for (var j = 0; j <= segments; j++)
                {
                    var angle = stepAngle * (segments * i + j);
                    switch (i)
                    {
                        case 0:
                            uiVertex.position = new UnityEngine.Vector2((halfWidth - minSide * ur) * +1F + minSide * ur * UnityEngine.Mathf.Cos(angle), (halfHeight - minSide * ur) * +1F + minSide * ur * UnityEngine.Mathf.Sin(angle)) + center;
                            break;
                        case 1:
                            uiVertex.position = new UnityEngine.Vector2((halfWidth - minSide * ul) * -1F + minSide * ul * UnityEngine.Mathf.Cos(angle), (halfHeight - minSide * ul) * +1F + minSide * ul * UnityEngine.Mathf.Sin(angle)) + center;
                            break;
                        case 2:
                            uiVertex.position = new UnityEngine.Vector2((halfWidth - minSide * ll) * -1F + minSide * ll * UnityEngine.Mathf.Cos(angle), (halfHeight - minSide * ll) * -1F + minSide * ll * UnityEngine.Mathf.Sin(angle)) + center;
                            break;
                        case 3:
                            uiVertex.position = new UnityEngine.Vector2((halfWidth - minSide * lr) * +1F + minSide * lr * UnityEngine.Mathf.Cos(angle), (halfHeight - minSide * lr) * -1F + minSide * lr * UnityEngine.Mathf.Sin(angle)) + center;
                            break;
                    }
                    uiVertex.uv0 = new UnityEngine.Vector2(UnityEngine.Mathf.InverseLerp(xMin, xMax, uiVertex.position.x), UnityEngine.Mathf.InverseLerp(yMin, yMax, uiVertex.position.y));
                    vh.AddVert(uiVertex);
                }

                int index;
                for (index = 1; index < (segments + 1) * 4; index++)
                {
                    vh.AddTriangle(0, index + 1, index);
                }
                vh.AddTriangle(0, 1, index);
            }
        }

        void UnityEngine.UI.ILayoutElement.CalculateLayoutInputHorizontal() { }

        void UnityEngine.UI.ILayoutElement.CalculateLayoutInputVertical() { }

        float UnityEngine.UI.ILayoutElement.minWidth => 0F;

        float UnityEngine.UI.ILayoutElement.minHeight => 0F;

        float UnityEngine.UI.ILayoutElement.preferredWidth => mainTexture.width;

        float UnityEngine.UI.ILayoutElement.preferredHeight => mainTexture.height;

        float UnityEngine.UI.ILayoutElement.flexibleWidth => -1F;

        float UnityEngine.UI.ILayoutElement.flexibleHeight => -1F;

        int UnityEngine.UI.ILayoutElement.layoutPriority => 0;
    }
}
